package com.horse.cloud.framework.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * @create: Created by intelliJIDEA18.1.3
 * @author: bing.Pan
 * @e-mail: 15923508369@163.com
 * @gmdate: 26/07/2018 16:57 星期四
 * @sidesc:
 */
public class DistributedReentrantLock implements Lock {

    private static final Logger LOGGER = LoggerFactory.getLogger(DistributedReentrantLock.class);


    /**
     * 默认锁定时间
     */
    private final static long DEFAULT_LOCK_EXPIRE_MILLISECOND = 1000 * 30;
    /**
     * 默认重试间隔时间
     */
    private final static long DEFAULT_RETRY_FREQUENCY_MILLISECOND = 100;


    /**
     * 锁名称
     */
    private String           lockName;

    /**
     * 随机不重复业务ID
     */
    private String           transactionId;

    /**
     *锁定毫秒时间
     */
    private long             lockExpireMillisecond;
    /**
     *重试间隔时间
     */
    private long             retryFrequencyMillisecond;

    private RedisReentrantLockClient redisReentrantLockClient;
    private volatile boolean locked;



    public DistributedReentrantLock(RedisReentrantLockClient redisReentrantLockClient,String lockName){
        this(redisReentrantLockClient, lockName, DEFAULT_LOCK_EXPIRE_MILLISECOND, DEFAULT_RETRY_FREQUENCY_MILLISECOND);
    }

    public DistributedReentrantLock(RedisReentrantLockClient redisReentrantLockClient, String lockName, long lockExpireMillisecond, long retryFrequencyMillisecond) {
        this.lockName = lockName;
        this.redisReentrantLockClient = redisReentrantLockClient;
        this.lockExpireMillisecond = lockExpireMillisecond;
        this.retryFrequencyMillisecond = retryFrequencyMillisecond;
    }



    @Override
    synchronized public void lock() {
        tryLock();
    }


    @Override
    synchronized public boolean tryLock() {
        if(locked){ return false; }
        transactionId = UUID.randomUUID().toString();
        boolean result = redisReentrantLockClient.setNX(lockName,transactionId,lockExpireMillisecond);
        if(result){
            locked = true;
            LOGGER.debug("【framework-redis】 分布式锁加锁成功, lockName [{}] ,transactionId [{}],lockExpireMillisecond [{}]",lockName,transactionId,lockExpireMillisecond);
        }else {
            LOGGER.debug("【framework-redis】 分布式锁加锁失败, lockName [{}] ,transactionId [{}],lockExpireMillisecond [{}]",lockName,transactionId,lockExpireMillisecond);
        }
        return result;

    }

    @Override
    synchronized public boolean tryLock(long time, TimeUnit unit) {
        long beginTime = System.currentTimeMillis();
        while (System.currentTimeMillis() - beginTime < unit.toMillis(time)) {
            if (this.tryLock()){
                return true;
            }
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(retryFrequencyMillisecond));
        }
        return false;
    }



    @Override
    synchronized public void lockInterruptibly() throws InterruptedException {
        throw new UnsupportedOperationException();
    }


    @Override
    synchronized public void unlock() {
        if(!locked){ return ;}
        if(transactionId.equals(redisReentrantLockClient.get(lockName))){
            redisReentrantLockClient.delete(lockName);
        }

    }

    @Override
    public Condition newCondition() {
        throw new UnsupportedOperationException();
    }
}
